#pragma once

#include <memory>
#include "CGALDefinition.h"
#include "MODELOBJDef.h"
#include <CGAL/Polyhedron_incremental_builder_3.h>

//////////////////////////////////////////////////////////////////////////
/*
reading obj and mtl to build Polyhedrons (HDS)
!note!obj should be grouped by solids
(for single solid or multi solid in which each solid inside of an obj generate one HDS)
.mtl here contain only the color of the face
John 101013
*/////////////////////////////////////////////////////////////////////////

#ifdef MYLIB_API_EXPORT
#define MYLIB_API __declspec(dllexport)
#else
#define MYLIB_API __declspec(dllimport)
#endif

//for debug purpose(set the first face index to 1)
//#define _DEBUG_START_FROM_RANDOM_FACE_INDEX

// whether to skip the non-2-manifold solid
//#define _DEBUG_SKIP_NON2MANIFOLD

// temp files
#define MIDFILEPATH "C:\\persim_mid.zjq"

//material name
#define LOGMTLNAME_INVALID "usemtl non_manifold\n"
#define LOGMTLNAME_VALID "usemtl manifold\n"
#define LOGMTLDEF_INVALID "newmtl non_manifold\n"
#define LOGMTLDEF_VALID "newmtl manifold\n"

//HDS builder
class Builder_onesolid_obj : public CGAL::Modifier_base<Polyhedron_3::HalfedgeDS>
{
private:
	typedef Polyhedron_3::HalfedgeDS HDS;
	typedef HDS::Vertex::Point Point;
	typedef HDS::Face_handle Face_handle;
	typedef CGAL::Polyhedron_incremental_builder_3<HDS> Builder;
	//
	FILE *m_pFile;
	FILE *m_midFile;
	//vertices number
	long m_pVtNum;
	//report whether the solid is manifold or not
	bool bIsValid;
	//whether output logfile
	bool bIsOutput;
	//
	bool bmtlFlag;//used to control the mtl in log file
	//
#ifdef _DEBUG_START_FROM_RANDOM_FACE_INDEX
	//for debug purpose(find the first face index and replace it to 1)
	int static_debug_flag;
#endif //_DEBUG_START_FROM_RANDOM_FACE_INDEX
	//materials
	std::vector<simple_material_obj>* materiallist;

public:
	Builder_onesolid_obj(FILE *pFile, std::vector<simple_material_obj>* mtl, FILE *m_mFile, long num = 0, bool isOutput = false);
	~Builder_onesolid_obj() {}
	//function object
	void operator()(HDS& hds);
	//whether this solid is non 2-manifold
	bool is_valid();
private:
	// read vertex coordinates
	void read_vertices(Builder &builder);
	// read facets and facet color
	bool read_facets(Builder &builder);
	//doing face adding
	bool add_facet(Builder &builder,char* pLine,bool bWithmtl,std::string &Mtl_name);
	//actually add face
	void add(Builder &builder, int* index, bool bWithmtl, std::string Mtl_name);
};

//IO class Polyhedron_3<SK> and Obj file
class MYLIB_API Parser_obj
{
public:
	Parser_obj() {logVertxNum=0; materiallist = NULL;}
	~Parser_obj() 
	{ 
		if (materiallist)
		{
			materiallist->clear();
			delete materiallist;
			materiallist = NULL;
		}
	}

	static Parser_obj* GetSingleton()
	{
		if (NULL == _instance.get())
		{
			_instance.reset(new Parser_obj);
		}
		return _instance.get();
	}

private:
	//all the materials
	typedef Polyhedron_3::Vertex_iterator Vertex_itr;
	typedef Polyhedron_3::Facet_iterator Face_itr;
	std::vector<simple_material_obj>* materiallist;
	//record the number of vertices of sections in log file
	int logVertxNum;
	
protected:
	//it should be initialized outside the definition
	static std::auto_ptr<Parser_obj> _instance;

public:
	//initiate Parser
	void init();
	/*read obj model
	* the obj files obey the 3dsmax 8 and 3dsmax 2009 style
	*/
	bool read_model_obj (const char*pFilename,const char *mtlFilename, ModelOBJ* obj, bool bIsOutput = false /*true output the nonmanifold facets*/);
 	// write a Model to an obj file
	bool write_obj(const char* pFilename, const ModelOBJ* obj, const bool bIsOpenOutput = false /*true output the open facets*/);
	//
	//grouping hds by materials (for rendering with VBO)
	//bool convert_hds_renderable(const ModelOBJ* input, Renderable_OBJ* output);
	//
	void convert_to_off(const SolidOBJ* solid, const char* pFilename);
	//
	void convert_off_to_obj(const char* pOffname, const char* pObjname);
	//
	//for representation of multiple models using Clr colorplatte file
	//input the pathname of the obj file and the clr file
	//output the modified obj file with the suffix "_vm" and output the corresponding mtl file
	void vary_mtl_for_obj(const char* pObjname, const char* pClrname, material_prop& mtlprop);

private:
	//assign the mtl name to the models in the obj
	bool vary_mtl_in_obj(const char* pObjname, std::vector<simple_material_obj>& mtllib);
	//write mtls in a mtllib to a mtl file
	bool write_mtl_to_file(const char* pObjname, material_prop& mtlprop, std::vector<simple_material_obj>& mtllib);
	//read colors in clr file to a mtllib(used in enrich_mtl_to_obj)
	bool read_clr_to_mtl(const char* pClrname, material_prop& mtlprop, std::vector<simple_material_obj>& mtllib);
	//read obj with absolute vertices index
	bool read_one_solid_translate(FILE* pFile, FILE* plogFile, SolidOBJ* solid, long pVtnum, bool isOutput, bool& isFinished);
	//read solid name from obj file and decide if the file has went to the end
	bool read_solid_name(FILE* pFile,FILE* pMidFile,SolidOBJ* solid, bool isOutput);
	//read material information
	bool read_materials(FILE* m_mtlFile);
	//write to solid the 2007 style
	void write_solid(FILE* fp, SolidOBJ* solid, int& vtnum, bool bIsOpenOutput /*validate closure*/);
	//write vertices
	void write_vertices(FILE* fp, SolidOBJ* solid);
	//write faces
	void write_faces(FILE* fp, SolidOBJ* solid, int& vtnum, bool bIsOpenOutput /*validate closure*/);

};
